﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Services;
using System.Reflection;
using Amf4Asmx.AmfObjects;

namespace Amf4Asmx.Web
{
    public class WebServiceData
    {
        public WebServiceData(Type compiledType)
        {
            WebServiceType = compiledType;
            WebMethods = new Dictionary<string, WebMethodData>();
            SoapHeaders = new Dictionary<string, PropertyInfo>();
            
            MethodInfo[] allWebMethods = GetWebMethods(WebServiceType.GetMethods());
            for (int i = 0; i < allWebMethods.Length; i++)
            {
                string name = WebMethodAttribute(allWebMethods[i]).MessageName;
                if (string.IsNullOrEmpty(name))
                {
                    name = allWebMethods[i].Name;
                }
                WebMethods.Add(name, new WebMethodData(allWebMethods[i]));
            }

            PropertyInfo[] allProperties = GetSoapHeaders(WebServiceType.GetProperties());
            for (int i = 0; i < allProperties.Length; i++)
            {
                SoapHeaders.Add(allProperties[i].Name, allProperties[i]);
            }
        }

        private MethodInfo[] GetWebMethods(MethodInfo[] methodInfo)
        {
            List<MethodInfo> methods = new List<MethodInfo>();
            foreach (MethodInfo method in methodInfo)
            {
                if (HasWebMethodAttribute(method))
                {
                    methods.Add(method);
                }
            }
            return methods.ToArray();
        }

        private PropertyInfo[] GetSoapHeaders(PropertyInfo[] propertyInfo)
        {
            List<PropertyInfo> properties = new List<PropertyInfo>();
            foreach (PropertyInfo property in propertyInfo)
            {
                if (typeof(System.Web.Services.Protocols.SoapHeader).IsAssignableFrom(property.PropertyType))
                {
                    properties.Add(property);
                }
            }
            return properties.ToArray();
        }

        public Type WebServiceType { get; private set; }
        public WebService CreateWebService(AmfHeader[] headers, AmfBody inBody)
        {
            WebMethodData target = WebMethods[inBody.Target];
            WebService result = Activator.CreateInstance(WebServiceType) as WebService;
            foreach (AmfHeader header in headers)
            {
                if (SoapHeaders.ContainsKey(header.Name) && target.ContainsSoapHeader(header.Name))
                {
                    SoapHeaders[header.Name].SetValue(result, TypeHelper.Instantiate(SoapHeaders[header.Name].PropertyType, header.Content), null);
                }
                else if (header.MustUnderstand)
                {
                    throw new InvalidOperationException("Could not understand header '" + header.Name + "'.");
                }
            }
            return result;
        }

        internal AmfHeader[] GetResultHeaders(object webService, AmfBody inBody)
        {
            List<AmfHeader> result = new List<AmfHeader>();
            WebMethodData target = WebMethods[inBody.Target];
            foreach (string headerName in target.OutSoapHeaders)
            {
                result.Add(new AmfHeader() 
                { 
                    Name = headerName, 
                    MustUnderstand = false, 
                    Content = SoapHeaders[headerName].GetValue(webService, null) 
                });
            }
            return result.ToArray();
        }

        public Dictionary<string, WebMethodData> WebMethods { get; private set; }
        public Dictionary<string, PropertyInfo> SoapHeaders { get; private set; }


        private bool HasWebMethodAttribute(MethodInfo m)
        {
            return m.GetCustomAttributes(typeof(WebMethodAttribute), true).Length > 0;
        }

        private WebMethodAttribute WebMethodAttribute(MethodInfo m)
        {
            return (WebMethodAttribute)m.GetCustomAttributes(typeof(WebMethodAttribute), true)[0];
        }



    }
}
